package cn.itzzx.beans.factory;

import cn.itzzx.annotation.Autowired;
import cn.itzzx.annotation.Repository;
import cn.itzzx.annotation.Service;
import cn.itzzx.annotation.Transactional;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Bean对象创建及依赖注入维护
 *
 * @author Martin
 * @date 2020-05-03
 * @see 1.0
 */
public class BeanFactory {
    /**
     * 存放包下的所有类
     */
    private volatile List<String> beanDefinitionNames = new ArrayList<>(256);

    private static Map<String, Object> beanDefinitionMap = new ConcurrentHashMap<>(256);

    /**
     * Bean对象创建及依赖注入维护
     *
     * @param scanPackage
     */
    public void refresh(String scanPackage) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        //初始化准备操作, 将该包的类 统一装载到 beanDefinitionNames
        scanner(scanPackage);

        //将 @Service @Repository的bean都实例化装载到 beanDefinitionMap
        handlerBean();

        //依赖注入
        autoInjection();

        //事务控制
        handlerTransactional();
    }

    /**
     * 事务处理
     */
    private void handlerTransactional() {
        ProxyFactory proxyFactory = (ProxyFactory) beanDefinitionMap.get("proxyFactory");

        Set<Map.Entry<String, Object>> entries = beanDefinitionMap.entrySet();

        for (Map.Entry<String, Object> entry : entries) {
            String key = entry.getKey();
            Object value = entry.getValue();
            Object proxy = null;
            //如果类上声明了注解的话，就不需要处理方法上的了
            if (value.getClass().isAnnotationPresent(Transactional.class)) {
                //判断是否实现接口，如果有实现接口使用jdk动态代理，如果没有实现接口的情况使用cglib
                if (value.getClass().getInterfaces() != null && value.getClass().getInterfaces().length > 0) {
                    proxy = proxyFactory.getJdkProxy(value);
                } else {
                    proxy = proxyFactory.getCglibProxy(value);
                }
                beanDefinitionMap.put(key, proxy);
                continue;
            } else {
                //获取所有属性 判断方法中是否含有 @Transaction 注解
                Method[] methods = value.getClass().getDeclaredMethods();
                Set<Method> set = null;
                for (Method method : methods) {
                    if (method.getDeclaredAnnotation(Transactional.class) != null) {
                        if (set == null) {
                            set = new HashSet<>();
                            set.add(getMethodForInterface(method, value.getClass().getInterfaces()));
                        }
                    }
                }
                if (set != null && set.size() > 0) {
                    //判断是否实现接口，如果有实现接口使用jdk动态代理，如果没有实现接口的情况使用cglib
                    if (value.getClass().getInterfaces() != null && value.getClass().getInterfaces().length > 0) {
                        proxy = proxyFactory.getJdkProxy(value, set);
                    } else {
                        proxy = proxyFactory.getCglibProxy(value, set);
                    }
                    beanDefinitionMap.put(key, proxy);
                }
            }

        }
    }

    /**
     * 从接口中获取method对象
     *
     * @param method
     * @param interfaces
     * @return
     */
    public static Method getMethodForInterface(Method method, Class<?>[] interfaces) {
        Method result = null;
        if (interfaces != null && interfaces.length > 0) {
            for (Class<?> class1 : interfaces) {
                try {
                    result = class1.getMethod(method.getName(), method.getParameterTypes());
                } catch (NoSuchMethodException e) {
                } catch (SecurityException e) {
                }
            }
        }
        if (result == null) {
            result = method;
        }
        return result;
    }

    /**
     * 依赖注入 @Autowired
     */
    private void autoInjection() {
        beanDefinitionMap.forEach((key, value) -> {
            //获取所有属性
            Field[] declaredFields = value.getClass().getDeclaredFields();
            for (Field declaredField : declaredFields) {
                //属性是否有 @Autowired 注解
                if (declaredField.isAnnotationPresent(Autowired.class)) {
                    //暴力
                    declaredField.setAccessible(true);
                    String fieldValue = declaredField.getAnnotation(Autowired.class).value();
                    if (fieldValue == null || "".equals(fieldValue)) {
                        fieldValue = declaredField.getName();
                    }
                    //从beanDefinitionMap找到对应的类赋值
                    try {
                        declaredField.set(value, beanDefinitionMap.get(fieldValue));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

    }

    private void handlerBean() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        if (beanDefinitionNames.isEmpty()) {
            return;
        }
        for (String beanDefinitionName : beanDefinitionNames) {
            //获取类中的所有属性
            Class<?> clazz = Class.forName(beanDefinitionName);

            //@Service  @Repository 注解
            if (clazz.isAnnotationPresent(Service.class)) {
                String beanName = clazz.getDeclaredAnnotation(Service.class).value();
                if (beanName == null || "".equals(beanName)) {
                    // beanName 首字母小写
                    beanName = lowerFirst(clazz.getSimpleName());
                }
                //保存到ioc 容器当中
                beanDefinitionMap.put(beanName, clazz.getDeclaredConstructor().newInstance());
            }

            //@Service  @Repository 注解
            if (clazz.isAnnotationPresent(Repository.class)) {
                String beanName = clazz.getDeclaredAnnotation(Repository.class).value();
                if (beanName == null || "".equals(beanName)) {
                    // beanName 首字母小写
                    beanName = lowerFirst(clazz.getSimpleName());
                }
                //保存到ioc 容器当中
                beanDefinitionMap.put(beanName, clazz.getDeclaredConstructor().newInstance());
            }
        }
    }


    public void scanner(String packageName) {
        //将 cn.itzzx.transfer 转换为 /cn/itzzx/transfer
        URL url = this.getClass().getClassLoader().getResource(packageName.replaceAll("\\.", "/"));
        File directory = new File(url.getFile());
        for (File file : directory.listFiles()) {
            //判断当前文件是否为文件夹，如果是继续递归调用
            if (file.isDirectory()) {
                scanner(packageName + "." + file.getName());
            } else {
                //拿到 className
                String beanName = packageName + "." + file.getName().replace(".class", "");
                beanDefinitionNames.add(beanName);
            }
        }
    }


    public static Object getBean(String id) {
        return beanDefinitionMap.get(id);
    }

    /**
     * 首字母转换小写
     *
     * @param str
     * @return
     */
    private String lowerFirst(String str) {
        char[] chars = str.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }

}
